#include "QueryExpressionParser.h"
#include "Parser.h"
using namespace std;

#include <iostream>
Parser::Parser(PKB* pkb){
	mPKB = pkb;
	// Debugging
	isStmtCounter = 0;
	isStmtLstCounter = 0;
}

//parse program and fill in relationtables (FOLLOW. PARENT)
bool Parser::executeSrc(string src){
	//cout << "PARSER: " << "Parsing executeSrc: " << endl;
	//cout << src << endl;
	//COMMENT OUT FOR DEBUGGING PARSING SRC
	src = removeCommentsFromSrc(src);
	//cout << "After removing comments from src: " << endl;
	//cout << src << endl;
	//END COMMENT OUT

	if (isProgram(src)) {
		//cout << "PARSER: " <<  "isProgram" << endl;

		//test all called procedures are valid
		//TODO: simplify codes of this module after debugging
		if (isCallValid(calledProc,validProc)) {
			//cout<<"PARSER: isCallValid"<<endl;
			//store call relation into PKB
			for(std::vector<ParserCallHelper>::iterator iter = callHelperTable.begin();iter!=callHelperTable.end();++iter) {
				//cout<<"set calls"<<endl;
				//SET CALLS
				int stmtNumber = iter->getStmtCounter();
				//Type::Entity::Line entityNumber =mPKB->getLineEntityByLine(iter->getStmtCounter());
				Type::Index::Proc caller = mPKB->getProcIndex(iter->getCaller());
				Type::Index::Proc called = mPKB->getProcIndex(iter->getCalled());
				
				mPKB->setCalls(caller,stmtNumber,called);

				// the following section is comment out since DE is doing the same thing
				////cout<<"getUses/Modifies"<<endl;
				////get all used/modified variables by calledProc
				////PKB got problem here
				//Type::List::Var usedVarList = mPKB->getUsedVarsByProc(called);
				//Type::List::Var modifiedVarList = mPKB->getModifiedVarsByProc(called);
				//
				////cout<<"setUses/Modifies"<<endl;
				////set all uses/modifies variables
				//for(size_t j = 0; j < usedVarList.size(); j++) {
				//	//cout<<"INSIDE use LOOP"<<endl;
				//	usedVarList[j];
				//	mPKB->setLineUses(stmtNumber, entityNumber, usedVarList[j]);
				//	mPKB->setProcUses(mPKB->getProcIndex(iter->getCaller()), usedVarList[j]);
				//}
				//for(size_t k = 0; k < modifiedVarList.size(); k++) {
				//	//cout<<"INSIDE modify LOOP"<<endl;
				//	modifiedVarList[k];
				//	mPKB->setLineModifies(stmtNumber, entityNumber, modifiedVarList[k]);
				//	mPKB->setProcModifies(mPKB->getProcIndex(iter->getCaller()), modifiedVarList[k]);
				//}
				////cout<<"FINISH setUses/Modifies"<<endl;
			}
			//cout<<"PARSER: PROGRAM PARSED!!!!"<<endl;
			return true;
		} else return false;
	}
	else return false;
}

//program
bool Parser::isProgram(string src) {
	//cout << "PARSER: " << "START parsing program ..."<<endl;
	stmtCounter = 0;
	totalProc = 0;
	remainSrc = src;
	TreeNode* procNode = NULL;
	//cout << "PARSER: " << "got PROGRAM NODE..."<<endl;
	pos = matchBraces(remainSrc);
	//cout << "PARSER: " << "isProgram POS: " << pos << endl;
	TreeNode* root = mPKB->createTreeNode(AST_PROGRAM);
	mPKB->setRoot(root);

	while (pos > 0){
		totalProc++;
		currProc = trim(remainSrc.substr (0,pos+1));
		remainSrc = trim(remainSrc.erase (0,pos+1));

		procNode = isProcedure (currProc);
		pos = matchBraces(remainSrc);
		//cout << "PARSER: " << "isProgram... "<<"match braces in loop POS: " << pos << endl;
		if (procNode!=NULL) {
			//cout << "PARSER: " << "current procedure node not null"<<endl;
			mPKB->linkNodes(root, procNode);

		} else {
			cout << "PARSER: " << "After isProgram ( but returned false for isProgram... )!!!\n\n";
			return false;
		}
	}
	if (remainSrc == "") {
		//cout << "PARSER: "<<"After isProgram; returned true"<<endl;
		return true;
	}
	return false;
}

TreeNode* Parser::isProcedure(string src) {
	//cout << "PARSER: " << "testing procedure..."<<endl;
	int lastLineOfContainer; // To be used to set the last line of container stmts
	layerCounter = 1;
	string token, myRemain;
	TreeNode* procedureNode,*stmtList;
	myRemain = trim(src);
	size_t currProcStartStmtCounter = stmtCounter+1;
	//cout << "PARSER: " << "AFTER testing procedure MyRemain: "<<endl;
	//cout << myRemain << endl;

	pos = myRemain.find(" ");

	if (pos != std::string::npos) {
		token = myRemain.substr(0, pos);
		//cout << "PARSER: " << "testing procedure..." << "AFTER posSPACE!=npos"<<endl;

		if (matchCaseInsensitive(token,"procedure")) {
			//cout << "PARSER: " << "testing procedure..." << "AFTER match PROCEDURE"<<endl;
			myRemain = trim(myRemain.erase(0,pos));
			pos = myRemain.find("{");
			//find procedure name
			if (pos != std::string::npos) {
				//cout << "PARSER: " << "testing procedure..." << "AFTER pos{!=npos"<<endl;
				procName = trim(myRemain.substr(0,pos));

				if (isName(procName)) {
					//cout << "PARSER: " << "testing procedure..." << "AFTER procedure isName"<<endl;
					//check whether this procedure is duplicated (not allowed)
					if (std::find(procNames.begin(),procNames.end(),procName)==procNames.end()) {
						//cout << "PARSER: " << "testing procedure..." << "AFTER checkDuplicates"<<endl;
						procNames.push_back(procName);
						//store into pkb
						procedureNode  = mPKB->createTreeNode(AST_PROCEDURE,mPKB->insertProc(procName));
						//update helper valid procedure list
						validProc.push_back(procName);
						//cout << "PARSER: " << "testing procedure..." << "AFTER checkDuplicates MyRemain: "<<endl;
						//cout << myRemain << endl;
						myRemain = trim(myRemain.erase(0,pos));
						pos = matchBraces(myRemain);
						//cout << "PARSER: " << "AFTER isName remainSrc: "<<myRemain<<endl;
						//cout << "PARSER: " << "AFTER isName pos: "<<pos<<endl;
						//cout << "PARSER: " << "AFTER isName myRemain.length()-1: "<<myRemain.length()-1<<endl;
						//IMPT \n is 1 char
						//current procedure
						if(pos==(myRemain.length()-1)) {
							//cout << "PARSER: " << "testing procedure..." << "AFTER Procedure {}"<<endl;
							//remove "{"
							myRemain = trim(myRemain.erase(0,1));
							//update current procedure src
							currProc = myRemain;
							//cout << "PARSER: " << "pos== remainSrc: "<<remainSrc<<endl;
							// link stmtList node to procedure root node
							stmtList = isStmtLst(myRemain, false,0, &lastLineOfContainer);
							//cout << "PARSER: " << "got stmtLst"<<endl;
							if (stmtList!=NULL) {
								//cout << "PARSER: " << "stmtList not null"<<endl;
								//PKB insertProcStartEnd
								//cout<<"mPKB->insertProcStartEnd(mPKB->getProcIndex(procName),currProcStartStmtCounter,stmtCounter): "<<endl;
								//cout<<"procName: "<<procName<<", currProcStartStmtCounter: "<<currProcStartStmtCounter<<", stmtCounter: "<<stmtCounter<<endl;
								//bool insertProcStartEnd(Type::Index::Proc, Type::Index::Line, Type::Index::Line);
								mPKB->insertProcStartEnd(mPKB->getProcIndex(procName),currProcStartStmtCounter,stmtCounter);
								mPKB->linkNodes(procedureNode, stmtList);
								//cout << "PARSER: " << "root"<<endl;
								return procedureNode;
							} else return NULL;
						}
					} else return NULL;
				}
			}
		}
	}
	return NULL;
}

//stmt list
TreeNode* Parser::isStmtLst(string src, bool isParent,int containerParentStmt, int *lastLineOfStmtLst){
	isStmtLstCounter++;
	int parentStmt = stmtCounter;// for setting parent relationship and modifies/uses relationship for containers

	if (isParent) {
		//set correct parent for if...then...else... stmt
		parentStmt=containerParentStmt;
	}

	//cout << "PARSER: " << "testing StmtLst..."<<endl;
	bool justEnteredStmtLst = true; // for setting follows relationship
	int prevStmt; // for setting follows relationship
	int localStmtCounterFollow = stmtCounter; // for setting follows relationship
	int localStmtCounterParent = stmtCounter; // for setting parent relationship
	int localStmtCounterModifies = stmtCounter; // for setting modifies relationship for containers
	int localStmtCounterUses = stmtCounter; // for setting uses relationship for containers

	string myRemain;
	string token;
	myRemain = trim(src);
	//cout << "PARSER: "<< "isStmtLst initial myRemain: "<< currProc << endl;

	TreeNode *stmtLstNode = NULL, *stmtNode = NULL;
	pos=currProc.find(";");

	//cout << "in isStmtLst BEFORE loop currProc: "<< currProc << endl;
	if (pos != std::string::npos){
		//token=stmt without ";"
		stmtLstNode = mPKB->createTreeNode(AST_STMTLST);
		while ( (pos != std::string::npos) && !match(currProc.substr(0,1),"}") ) {
			//cout << "in isStmtLst JUST ENTER loop currProc: "<< currProc << endl;
			// keep parsing stmts
			stmtCounter++;
			if (justEnteredStmtLst) {
				// initialize prevStmt for setting follows relationships
				prevStmt = stmtCounter;
			}

			stmtNode = isStmt(currProc, lastLineOfStmtLst);
			//cout << "PARSER: "<< "in isStmtLst AFTER isStmt in loop currProc: "<< currProc << endl;

			if (stmtNode == NULL) {
				return NULL;
			}

			mPKB->linkNodes(stmtLstNode, stmtNode);
			//this pos is redundant????
			pos = currProc.find(";");
			localStmtCounterFollow++;
			localStmtCounterParent++;
			localStmtCounterModifies++;
			localStmtCounterUses++;

			// Set parent relationship
			if (isParent && (localStmtCounterParent == stmtCounter)) {
				// Normal line after line parsing
				//cout << "mPKB->setParent1(parentStmt, localStmtCounterParent)" << endl;
				//cout << "parentStmt: "<<parentStmt<<", localStmtCounterParent: "<<localStmtCounterParent<<endl;

				mPKB->setParent(parentStmt, localStmtCounterParent);
			} else if (isParent && (localStmtCounterParent != stmtCounter)) {
				// sudden jump in parsing, meaning there was a nested stmtLst
				//cout << "mPKB->setParent2(parentStmt, localStmtCounterParent)" << endl;
				//cout << "parentStmt: "<<parentStmt<<", localStmtCounterParent: "<<localStmtCounterParent<<endl;

				mPKB->setParent(parentStmt, localStmtCounterParent);
				localStmtCounterParent = stmtCounter;
			}

			// Set follows relationship
			if (!justEnteredStmtLst && (localStmtCounterFollow == stmtCounter)) {
				// Normal line after line parsing
				//cout<<"mPKB->setFollows1(prevStmt, localStmtCounterFollow)"<<endl;
				//cout<<"prevStmt: "<<prevStmt<<", localStmtCounterFollow: "<<localStmtCounterFollow<<endl;

				mPKB->setFollows(prevStmt, localStmtCounterFollow);
				prevStmt = localStmtCounterFollow;
			} else if (!justEnteredStmtLst && (localStmtCounterFollow != stmtCounter) ) {
				//cout<<"mPKB->setFollows2(prevStmt, localStmtCounterFollow)"<<endl;
				//cout<<"prevStmt: "<<prevStmt<<", localStmtCounterFollow: "<<localStmtCounterFollow<<endl;

				mPKB->setFollows(prevStmt, localStmtCounterFollow); // localStmtCounter holds the stmt number of the parent stmt
				prevStmt = localStmtCounterFollow;
				localStmtCounterFollow = stmtCounter;
			} else if (justEnteredStmtLst && (localStmtCounterFollow != stmtCounter) ) {
				// Special case when there is a container stmt immediately upon entering a container stmt
				localStmtCounterFollow = stmtCounter;
				justEnteredStmtLst = false;
			} else {
				justEnteredStmtLst = false;
			}

			// Set Modifies relationship for containers
			std::vector<int> modifiedIndexes;
			if (localStmtCounterModifies == stmtCounter) {
				// normal line after line parsing
				//cout<<"isStmtLst modifiedIndexes localStmtCounterModifies 1: "<<localStmtCounterModifies<<endl;
				modifiedIndexes = mPKB->getModifiedVarsByLine(localStmtCounterModifies);
			} else {
				// sudden jump in parsing, meaning there was a container being parsed
				//cout<<"isStmtLst modifiedIndexes localStmtCounterModifies 2: "<<localStmtCounterModifies<<endl;
				modifiedIndexes = mPKB->getModifiedVarsByLine(localStmtCounterModifies);
				localStmtCounterModifies = stmtCounter;
			}
			if (isParent) {
				for (unsigned int i = 0; i < modifiedIndexes.size(); i++) {
					int modifiedIndex = modifiedIndexes.at(i);
					//updated API for iter2
					Type::Entity::Line entityNumber = mPKB->getLineEntityByLine(parentStmt);
					//setModifies by line
					mPKB->setLineModifies(parentStmt, entityNumber, modifiedIndex);
				}
			}

			// Set Uses relationship for containers
			std::vector<int> usedIndexes;
			if (localStmtCounterUses == stmtCounter) {
				// normal parsing line after line
				//cout<<"isStmtLst usedIndexes localStmtCounterUses 1: "<<localStmtCounterUses<<endl;
				usedIndexes = mPKB->getUsedVarsByLine(localStmtCounterUses);
			} else {
				// sudden jump in parsing, meaning there was a container being parsed
				//cout<<"isStmtLst usedIndexes localStmtCounterUses 1: "<<localStmtCounterUses<<endl;
				usedIndexes = mPKB->getUsedVarsByLine(localStmtCounterUses);
				localStmtCounterUses = stmtCounter;
			}
			if (isParent) {
				for (unsigned int i = 0; i < usedIndexes.size(); i++) {
					int usedIndex = usedIndexes.at(i);
					Type::Entity::Line entityNumber = mPKB->getLineEntityByLine(parentStmt);
					//set uses by line
					mPKB->setLineUses(parentStmt, entityNumber, usedIndex);
				}
			}
		}
		//cout << "PARSER: " << "FOUND } at stmtCounter: " << stmtCounter << endl;
		//current procedure continues
		if (pos != std::string::npos) {
			//cout << "PARSER: " << "isStmtLst... can find ;"<< endl;
			pos = currProc.find("}");
			//cout << "PARSER: " << "isStmtLst... can find }"<< endl;
			myRemain = myRemain.substr(pos, myRemain.length());
			//cout << "PARSER: " << "isStmtLst... AFTER last stmt myRemain"<< currProc << endl;
		}
		if ( match(currProc.substr(0,1),"}") ) {
			//cout << "PARSER: " <<  "\"}\" MATCH CHECKING at remainSrc: (SUCCESS) " << currProc << endl;
			currProc = trim(currProc.erase(0,1));
			//cout << "PARSER: " <<  "After removing one \"}\" from remainSrc: " << currProc << endl;
			return stmtLstNode;
		} else {
			//cout << "PARSER: " <<  "\"}\" MATCH CHECKING at remainSrc: (FAILURE) " << currProc << endl;
		}

	}
	//cout << "PARSER: " << "not stmtLst :\n";
	return NULL;
}

TreeNode * Parser::isStmt(string src, int *lastLineOfContainer) {
	isStmtCounter++;
	int thisStmtNumber = stmtCounter;
	//cout << "PARSER: " << "testing Stmt..."<<endl;
	TreeNode *callStmt, *assignStmt, *whileStmt, *ifStmt;
	//cout << "PARSER: stmtNumber: "<<stmtCounter<<endl;
	//cout << "PARSER: " <<  "src before isAssign: " << src << endl;
	assignStmt = isAssign(src);
	//cout << "PARSER: " <<  "src after checking isAssign: " << src << endl;
	if (assignStmt != NULL) {
		//cout << "PARSER: " <<thisStmtNumber<< " ASSIGN STMT NOT NULL"<<endl;
		mPKB->insertNode(thisStmtNumber, assignStmt);
		// As long as the current stmt is not a stmtLst, we need to change the last line of the container to this stmt
		*lastLineOfContainer = thisStmtNumber;
		return assignStmt;
	}

	//cout << "PARSER: " <<  "ASSIGN STMT IS NULL" << endl;
	//cout << "PARSER: " <<  "src before isCall: " << src << endl;
	callStmt = isCall(src);
	//cout << "PARSER: " <<  "src after checking isCall: " << src << endl;
	if (callStmt != NULL) {
		//cout << "PARSER: "<<thisStmtNumber << " CALL STMT NOT NULL"<<endl;
		//store info into PKB is post-parsing (in isProgram)
		// As long as the current stmt is not a stmtLst, we need to change the last line of the container to this stmt
		*lastLineOfContainer = thisStmtNumber;
		return callStmt;
	}

	//cout << "PARSER: " << "CALL STMT IS NULL"<<endl;
	//cout << "PARSER: " <<  "src before checking isWhile: " << src << endl;
	whileStmt = isWhile(src, lastLineOfContainer);
	//cout << "PARSER: " <<  "after checking isWhile..." << endl;
	//cout << "PARSER: " <<  "layerCounter: " << layerCounter << endl;
	if (whileStmt != NULL) {
		//cout << "PARSER: "<<thisStmtNumber <<  " whileStmt NOT NULL" << endl;
		mPKB->insertNode(thisStmtNumber, whileStmt);
		return whileStmt;
	}
	//cout << "PARSER: " <<  "whileStmt IS NULL" << endl;
	//cout << "PARSER: " <<  "src before checking isIf: " << src << endl;
	ifStmt = isIf(src, lastLineOfContainer);
	//cout << "PARSER: " <<  "src after checking isIf: " << src << endl;
	if (ifStmt != NULL) {
		//cout << "PARSER: "<<thisStmtNumber <<  " ifStmt NOT NULL" << endl;
		mPKB->insertNode(thisStmtNumber, ifStmt);
		return ifStmt;
	}
	//cout << "PARSER: " <<  "ifStmt IS NULL" << endl;
	return NULL;
}

TreeNode* Parser::isCall(string src){
	//cout << "PARSER: " << "testing isCALL..." <<endl;
	TreeNode *callNode;

	string token,myRemain;
	size_t myPos;

	myPos = src.find(";");
	myRemain = trim(src.substr(0,myPos));
	//cout << "PARSER: " << "isCall myRemain BEFORE: "<< myRemain << endl;
	myPos = src.find(" ");

	if (myPos != std::string::npos) {
		token = myRemain.substr(0, myPos);

		if (match(token,"call")) {
			//myRemain is now expected to be called procedure name
			myRemain = trim(myRemain.erase(0,4));
			//update currProc
			currProc = trim(currProc.erase(0,4));
			//cout << "PARSER: " << "isCall myRemain ERASE \"call\": "<< myRemain << endl;

			//avoid recursion
			if(isName(myRemain)&&(myRemain!=procName)) {
				//cout << "isCall valid"<< endl;
				//myRemain : called procedureName
				//insert called procedure into callNode, but not proceTable in case of non-exist procedures
				calledProc.push_back(myRemain);
				callNode = mPKB->createTreeNode(AST_CALL,mPKB->insertProc(myRemain));
				mPKB->insertLineEntity(stmtCounter, Type::Entity::CALL_LINE_ENTITY);

				//insert call info into parser helper triplet vector
				ParserCallHelper helper = ParserCallHelper(procName,stmtCounter,myRemain);
				callHelperTable.push_back(helper);
				//update currProc
				//erase called proc name
				currProc = trim(currProc.erase(0,myRemain.length()));
				//erase ";"
				currProc = trim(currProc.erase(0,1));
				return callNode;
			}
		}
	}
	return NULL;
}

//assignment in prototype
TreeNode* Parser::isAssign(string src){
	//cout << "PARSER: " << "testing ASSIGN..."<<endl;
	TreeNode *assignStmt = mPKB->createTreeNode(AST_ASSIGN), *varNode;
	TreeNode *plusNode = mPKB->createTreeNode(AST_PLUS);

	string myRemain;
	string token;
	string modifiedVar;
	string usedVar;
	vector<string> usedVarSet;
	size_t myPos;
	int procIndex = mPKB->getProcIndex(procName);

	myPos = src.find(";");
	token = trim(src.substr(0,myPos));
	//cout << "PARSER: " << "ASSIGN token: "<<token<<endl;
	myRemain = trim(src.substr(myPos+1, src.length()));
	//cout << "PARSER: " << "after ASSIGN token MyRemain: " << myRemain << endl;
	pos = token.find("=");

	if (pos != std::string::npos&&isName(modifiedVar = trim(token.substr(0, pos)))) {

		varNode = mPKB->createTreeNode(AST_VAR,mPKB->insertVar(modifiedVar));
		mPKB->linkNodes(assignStmt, varNode);

		//remove "var = "
		token = trim(token.substr(pos+1,token.length()));

		usedVarSet = insertVariablesAndConstantsIntoPKB(token);

		// Convert the expression contained in token into a tree and link it as a child of the assignStmt!
		QueryExpressionParser *queryExpParser = new QueryExpressionParser(mPKB);
		//cout << "PARSER: " <<  "converting expression to tree with QueryExpressionParser: " << token << endl;
		TreeNode* exprRoot = queryExpParser->convertExpressionToTree(token);
		if (exprRoot == NULL) {
			// invalid expression, return NULL
			//cout << "invalid expression" << endl;
			return NULL;
		} else {
			// valid expression
			//cout << "valid expression" << endl;
			mPKB->linkNodes(assignStmt, exprRoot);
		}

		//cout << "PARSER: " << "before pkb"<<endl;
		//cout << "PARSER: " << "stmtCounter: "<<stmtCounter<<endl;
		//cout << "PARSER: " << "procIndex: "<<mPKB->getProcIndex(procName)<<endl;
		//cout << "PARSER: " << "varIndex: "<<mPKB->getVarIndex(modifiedVar)<<endl;
		//cout << "PARSER: " << "Before inserting..."<<endl;
		
		//InsertLineEntity( progline, lineEntity)
		//cout << "mPKB->insertLineEntity(stmtCounter,LINE_ENTITY): "<<stmtCounter<<", ASSIGNMENT_LINE_ENTITY"<<endl;
		mPKB->insertLineEntity(stmtCounter,Type::Entity::ASSIGNMENT_LINE_ENTITY);
		//cout << "PARSER: " << "pass set insertLineEntity"<<endl;

		//set modifies by line
		//cout << "mPKB->setLineModifies: modifiedVar: " << modifiedVar<<endl;
		int modifiedIndex = mPKB->getVarIndex(modifiedVar);
		mPKB->setLineModifies(stmtCounter,Type::Entity::ASSIGNMENT_LINE_ENTITY,modifiedIndex);
		//set modifies by proc
		mPKB->setProcModifies(procIndex, modifiedIndex);
		//cout << "PARSER: " << "pass set modify"<<endl;

		//setUses for all variables in usedVarSet
		for(unsigned int i =0;i<usedVarSet.size();i++) {
			varName = usedVarSet.at(i);
			//cout << "mPKB->setLineUses: usedVar: " <<varName<<endl;
			int usedIndex = mPKB->getVarIndex(varName);
			mPKB->setLineUses(stmtCounter, Type::Entity::ASSIGNMENT_LINE_ENTITY, usedIndex);
			//set uses by proc
			mPKB->setProcUses(procIndex, usedIndex);
		}

		//update currProc
		currProc = trim(currProc.substr(myPos+1,currProc.length()));
		stmtTable[stmtCounter] = layerCounter;
		//cout << "PARSER: " << "AFTER ASSIGN myRemain: "<<myRemain<<endl;
		//cout << "PARSER: " << "AFTER ASSIGN currProc: "<<currProc<<endl;
		return assignStmt;
	}

	//cout << "PARSER: " << "not assign :\n";
	return NULL;
}

//while stmt
TreeNode* Parser::isWhile(string src, int *lastLineOfContainer) {
	//cout << "PARSER: " << "test WHILE..."<<endl;
	//cout << "PARSER: " << "stmtCounter: " << stmtCounter << endl;
	string token;
	string myWhile = trim(src);
	TreeNode* whileNode = mPKB->createTreeNode(AST_WHILE),*varNode, *stmtLstNode;
	Type::Index::Var varIndex;
	int procIndex = mPKB->getProcIndex(procName);

	int containerParentStmt = stmtCounter;
	//cout << "PARSER: " <<  "myWhile: " << myWhile << endl;
	//cout << "PARSER: " << "ENTERED isWhile currProc : " << currProc << endl;

	if ((pos = myWhile.find(" ")) != std::string::npos) {
		token = myWhile.substr(0, pos);
		//cout << "PARSER: " <<  "myWhile TOKEN: " << token << endl;
		if (match(token,"while")) {
			//erase "while"
			//cout << "PARSER: " <<  "while token matched" << endl;
			myWhile = trim(myWhile.erase(0,pos));
			currProc  = trim(currProc.erase(0,pos));

			if ((pos = myWhile.find("{")) != std::string::npos) {
				pos = myWhile.find("{");
				varName = trim(myWhile.substr(0,pos));
				//cout<<"PARSER: WHILE varname = "<<varName<<endl;
				//cout << "PARSER: " << "ENTERED isWhile AFTER erasing \" while \" currProc : " << currProc << endl;

				if (isName(varName)) {
					//cout << "PARSER: " <<  "in isWhile, isName" << endl;
					//cout << "PARSER: " <<  "varName is a name: " << varName << endl;
					token = token+" "+varName;

					varIndex = mPKB->insertVar(varName);
					varNode = mPKB->createTreeNode(AST_VAR,varIndex);
					mPKB->linkNodes(whileNode, varNode);

					//InsertLineEntity
					//cout << "mPKB->insertLineEntity(stmtCounter,LINE_ENTITY): "<<stmtCounter<<", WHILE_LINE_ENTITY"<<endl;
					mPKB->insertLineEntity(stmtCounter,Type::Entity::WHILE_LINE_ENTITY);
					Type::Entity::Line entityNumber = mPKB->getLineEntityByLine(stmtCounter);

					//cout << "mPKB->setLineUses: usedVar: " <<varName<<endl;
					mPKB->setLineUses(stmtCounter, entityNumber, varIndex);
					//set uses by proc
					mPKB->setProcUses(procIndex ,varIndex);

					//erase varName
					myWhile = trim(myWhile.erase(0,pos));
					int currProcOpenBracketPos = currProc.find("{");
					//erase "while varName {"
					currProc = trim(currProc.erase(0,currProcOpenBracketPos+1));
					//cout << "PARSER: " << "ENTERED isWhile AFTER erasing \"while varName {\" currProc : " << currProc << endl;

					stmtTable[stmtCounter]=layerCounter;
					//cout << "PARSER: " <<  "Before matchBraces(myWhile), myWhile: \n" << myWhile << endl;
					pos = matchBraces(myWhile);

					//cout << "PARSER: " <<  "myWhile length = " << myWhile.length() << endl;
					//cout << "PARSER: " <<  "myWhile matching end bracket pos = " << pos << endl;

					//cout << "PARSER: " <<  "myWhile WITHOUT {: " << myWhile << endl;
					if(pos > 0) {
						//myWhile is this while loop without {
						myWhile = trim(myWhile.substr(1,pos));
						//cout << "PARSER: " <<  "getting stmtLstNode with myWhile: " << myWhile << endl;
						stmtLstNode = isStmtLst(myWhile, true,containerParentStmt, lastLineOfContainer);
						if (stmtLstNode!=NULL){
							//cout << "PARSER: " <<  "stmtLstNode NOT NULL" << endl;
							mPKB->linkNodes(whileNode, stmtLstNode);
							stmtTable[stmtCounter] = layerCounter;
							//cout << "PARSER: " << "end WHILE currProc: "<<currProc<<endl;
							// Set the last line of this while stmt
							//cout << "setLastOfWhile(" << containerParentStmt << ", " << *lastLineOfContainer << ")\n";
							mPKB->setLastOfWhile(containerParentStmt, *lastLineOfContainer);
							return whileNode;
						}
					}
				}
			}
		}
	}
	return NULL;
}

//if stmt
TreeNode* Parser::isIf(string src, int *lastLineOfContainer) {
	//"if" ifVarName "then" {stmtLst} "else" {stmtLst}
	//cout << "PARSER: " << "test IF..." << endl;
	string token,myRemain;
	size_t myPos;
	Type::Index::Var varIndex;
	int procIndex = mPKB->getProcIndex(procName);
	TreeNode* ifNode = mPKB->createTreeNode(AST_IF),*varNode, *thenStmtLstNode, *elseStmtLstNode;

	myRemain = trim(src);
	currProc = trim(currProc);
	int containerParentStmt = stmtCounter;

	if (myPos = myRemain.find(" ") != std::string::npos) {
		//cout << "PARSER: " << "in isIf AFTER matching first space"<< endl;
		//token ="if"
		myPos = currProc.find(" ");
		token = trim(currProc.substr(0,myPos));
		//myRemain = "(varName) then {...} else {...}"
		currProc = trim(currProc.erase(0,myPos+1));

		if (matchCaseInsensitive(token,"if")) {
			//cout << "PARSER: " << "in isIf AFTER matching if"<< endl;
			if(myPos = currProc.find(" ") != std::string::npos) {
				//token = varName
				myPos = currProc.find(" ");
				token = trim(currProc.substr(0,myPos));
				//cout << "PARSER: " << "in isIf AFTER matching if, varName: "<< token << endl;
				//myRemain = "then {...}else{...}"
				currProc = trim(currProc.erase(0,myPos));

				if(isName(token)) {
					//cout << "PARSER: " << "in isIf AFTER matching varName: "<<token << endl;
					//store into AST
					varIndex = mPKB->insertVar(token);
					varNode = mPKB->createTreeNode(AST_VAR,varIndex);
					mPKB->linkNodes(ifNode,varNode);

					//insertLineEntity
					//cout << "mPKB->insertLineEntity(stmtCounter,LINE_ENTITY): "<<stmtCounter<<", IF_LINE_ENTITY"<<endl;
					mPKB->insertLineEntity(stmtCounter,Type::Entity::IF_LINE_ENTITY);
					Type::Entity::Line entityNumber = mPKB->getLineEntityByLine(stmtCounter);

					//setLineUses
					//cout << "mPKB->setLineUses: usedVar: " <<token<<endl;
					mPKB->setLineUses(stmtCounter, entityNumber, varIndex);
					//set uses by proc
					mPKB->setProcUses(procIndex ,varIndex);
					myPos = currProc.find("{");
					if (myPos != std::string::npos) {
						//cout << "PARSER: " << "in isIf AFTER matching {, myPos: "<< myPos << endl;
						//token = "then"
						token = trim(currProc.substr(0,myPos));
						//myRemain = "{...}else{...}"
						currProc = trim(currProc.erase(0,myPos));

						if (matchCaseInsensitive(token,"then")) {
							//cout << "PARSER: " << "in isIf AFTER matching then"<< endl;
							myPos = matchBraces(currProc);
							//cout << "PARSER: " << "in isIf AFTER matching then, myPos: "<<myPos<< endl;

							if (myPos != std::string::npos) {
								//token = "...}"
								token = trim(currProc.substr(1,myPos));
								//myRemain = "...} else {...}"
								currProc = trim(currProc.erase(0,1));

								thenStmtLstNode = isStmtLst(token, true, containerParentStmt, lastLineOfContainer); // note: lastLineOfIf returned here is not the last line. The last line should be from the else stmtLst
								//cout << "PARSER: "<<"in isIf AFTER thenStmtLstNode"<<endl;
								int lastLineOfThen = *lastLineOfContainer;
								if (thenStmtLstNode != NULL){
									//cout << "PARSER: "<<"in isIf AFTER thenStmtLstNode NOT NULL"<<endl;
									mPKB->linkNodes(ifNode,thenStmtLstNode);
									stmtTable[stmtCounter] = layerCounter;

									myPos=currProc.find("{");
									//cout << "PARSER: "<<"in isIf AFTER thenStmtLstNode NOT NULL, myPos: "<<myPos<<endl;
									if (myPos!=std::string::npos) {
										//token="else"
										token = trim(currProc.substr(0,myPos));
										//currProc="{...}..."
										currProc = trim(currProc.erase(0,myPos));

										if (matchCaseInsensitive(token,"else")) {
											myPos = matchBraces(currProc);
											if (myPos != std::string::npos) {
												//token = "...}"
												token = trim(currProc.substr(1,myPos));
												//currProc = "...} ..."
												currProc = trim(currProc.erase(0,1));
												elseStmtLstNode = isStmtLst(token,true,containerParentStmt, lastLineOfContainer);
												//cout << "PARSER: "<<"in isIf AFTER elseStmtLstNode"<<endl;
												if (elseStmtLstNode != NULL) {
													//cout << "PARSER: "<<"in isIf AFTER elseStmtLstNode NOT NULL"<<endl;
													mPKB->linkNodes(ifNode,elseStmtLstNode);
													stmtTable[stmtCounter] = layerCounter;
													// Set the last line of this if stmt
													//cout << "setLastOfIf(" << containerParentStmt << ", " << lastLineOfThen << ", " << *lastLineOfContainer << ")\n";
													mPKB->setLastOfIf(containerParentStmt, lastLineOfThen, *lastLineOfContainer);
													return ifNode;
												}
											}
										}
									}
								}
							}
						}
					}
				}
			}

		}
	}
	return NULL;
}

//TESTED
bool Parser::isName(string str) {
	if (str.length() == 0) {
		// empty string is not a name
		return false;
	}

	char firstChar = str.at(0);
	if (!isLetter(firstChar)) {
		// variable name needs to start with a letter
		return false;
	}
	for (unsigned int i = 1; i < str.length(); i++) {
		char ch = str.at(i);
		if ( !isDigit(ch) && !isLetter(ch) ) {
			return false;
		}
	}
	return true;
}

//starting from open brace, returns the FIRST position of matching close brace in integer.
//counting from 0
int Parser::matchBraces(string src) {
	size_t count_round = 0;
	size_t count_left_brace = 0;
	size_t count_right_brace = 0;

	while (src != ""){
		count_round ++;
		if(src[0] == LEFT_BRACE) count_left_brace ++;
		if(src[0] == RIGHT_BRACE) count_right_brace ++;
		if(count_left_brace == count_right_brace&&count_left_brace!=0) return count_round-1;
		src = src.erase(0,1);
	}

	return -1;
}

//TESTED
bool Parser::isNum(string str){
	if (str.length() == 0) {
		// empty string is not a number
		return false;
	}
	for (size_t i = 0; i < str.length(); i++){
		if(!isDigit(str.at(i))) return false;
	}
	return true;
}

//TESTED
bool Parser::isLetter(char ch) {
	if (ch>='A' && ch<='Z') return true;
	else if (ch>='a' && ch<='z') return true;
	return false;
}

//TESTED
bool Parser::isDigit(char ch){
	if (ch>='0' && ch<='9') return true;
	return false;
}

//TESTED
bool Parser::match(string matcher, string matchee) {
	if (matcher.compare(matchee) == 0) return true;
	else return false;
}

bool Parser::matchCaseInsensitive(string matcher, string matchee) {
	matcher = toLowerCaseString(matcher);
	matchee = toLowerCaseString(matchee);
	if (matcher.compare(matchee) == 0) return true;
	else return false;
}

bool Parser::isCallValid(vector<string> called, vector<string> valid){
	for(unsigned int i=0;i<called.size();i++) {
		//valid procedure does not contain called procedure
		if(std::find(valid.begin(),valid.end(),called[i])==valid.end()) {
			return false;
		}
	}
	return true;
}

//TESTED
string Parser::trim(const std::string& str) {
	const std::string& whitespace = " \t\n\r";
	const auto strBegin = str.find_first_not_of(whitespace);
	if (strBegin == std::string::npos)
		return ""; // no content

	const auto strEnd = str.find_last_not_of(whitespace);
	const auto strRange = strEnd - strBegin + 1;

	return str.substr(strBegin, strRange);
}

std::string Parser::toLowerCaseString(std::string s) {
	std::transform(s.begin(), s.end(), s.begin(), ::tolower);
	return s;
}

std::string Parser::removeCommentsFromSrc(std::string src) {
	unsigned int commentPos = src.find("//");
	while (commentPos != string::npos) {
		// need to remove comment!
		// find '\n' character which signifies next line that comes after the comment symbol "//"
		unsigned int nextLinePos = src.find('\n', commentPos);
		if (nextLinePos == string::npos) {
			src = src.erase(commentPos, nextLinePos); // erase until the end
		} else {
			src = src.erase(commentPos, (nextLinePos-commentPos+1));
		}
		commentPos = src.find("//");
	}
	return src;
}

std::string Parser::extractFirstNameOrNumber(std::string *str) {
	while ( str->length() != 0 ) {
		// keep getting the first char and checking if it is a digit/letter
		char firstChar = str->at(0);
		if ( !isDigit(firstChar) && !isLetter(firstChar) ) {
			// throw the first character away because it can't be the start of a name or a constant!
			str->erase(0, 1);
			continue;
		} else {
			// Here we need to find the position at which the name/number ends, and return the name/number
			unsigned int position = 1;
			if ( isDigit(firstChar) ) {
				// is number! (first char is digit)
				// find the position at which this number ends
				while ( position < str->length() ) {
					char ch = str->at(position);
					if ( !isDigit(ch) ) {
						break;
					}
					position++;
				}
			} else {
				// is name! (first char is letter)
				// find the position at which this name ends
				while ( position < str->length() ) {
					char ch = str->at(position);
					if ( !isDigit(ch) && !isLetter(ch) ) {
						break;
					}
					position++;
				}
			}
			// Once here, we have found the pos at which the name/number ends
			// just remove that from the string and return it!
			std::string nameOrNumber = str->substr(0, position);
			str->erase(0, position);
			return nameOrNumber;
		}
	}
	// Out of the while loop, it means we erased everything in the string and didn't find a name or a number
	return "";
}

std::vector<std::string> Parser::insertVariablesAndConstantsIntoPKB(std::string expr) {
	//cout << "Expression given = " << expr << endl;
	std::vector<std::string> usedVarSet;
	while (expr.length() != 0) {
		std::string token = extractFirstNameOrNumber(&expr);
		//cout << "Extracted token = " << token << endl;
		if (token.length() == 0) {
			return usedVarSet;
			//return std::vector<std::string>();
		}
		if (isName(token)) {
			std::string varName = token;
			mPKB->insertVar(varName);
			usedVarSet.push_back(varName);
		} else if (isNum(token)) {
			int constant = mystoi(token);
			mPKB->insertConstant(stmtCounter, Type::Entity::ASSIGNMENT_LINE_ENTITY, constant);
		}
	}
	/*cout << "returning usedVarSet containing : ";
	for (unsigned int i = 0; i < usedVarSet.size(); i++) {
	cout << usedVarSet.at(i) << " ";
	}
	cout << endl;*/
	return usedVarSet;
}

int Parser::mystoi(std::string s) {
	if (s.length() == 0) {
		return 0;
	}
	char firstDigit = s.at(0);
	if (firstDigit == '0') {
		// find first instance of non-'0'
		int pos = s.find_first_not_of('0');
		s = s.erase(0, pos);
		if (s.length() == 0) {
			return 0;
		} else {
			return std::stoi(s);
		}
	} else {
		return std::stoi(s);
	}
}
